Golang基础知识学习笔记

您所在的位置:网站首页 golang 基础教程 Golang基础知识学习笔记

Golang基础知识学习笔记

2023-12-03 16:27| 来源: 网络整理| 查看: 265

写在前面

最近做一个小程序后端项目,准备用go语言编写,所以学习了一下go语言基础,做了一些笔记,备忘,也供大家参考。 参考资料:Go语言教程|菜鸟教程 作者水平有限,有任何问题可以在文章下方给我留言,谢谢!

笔记目录 写在前面1、Go语言结构2、执行go程序3、Go的变量声明1)声明变量并指定类型2)声明变量不指定类型3)省略var,使用:=4)多变量声明 4、Go的常量常量的定义特殊常量—iota 5、Go的条件判断语句switch语句Type Switchswitch中的fallthrough关键字 select语句 6、Go的循环语句for循环1)如同c语言的for2)如同c语言的while3)for range 循环控制语句 7、Go的函数函数定义匿名函数Go函数闭包Go的方法 8、Go的数组多维数组 9、Go的指针基本用法空指针指针数组双重指针 10、Go的结构体11、Go的切片(Slice)定义切片切片初始化创建切片(未赋值)直接初始化截取数组来初始化通过切片s初始化切片s1 len()和cap()函数append() 和 copy() 函数 12、Go的Map(集合)Map的声明Map的初始化delete()函数Map的创建和使用 13、Go的range14、Go的类型转换15、Go的接口(interface)16、Go错误处理17、Go并发通道(channel)通道缓冲区遍历通道与关闭通道

1、Go语言结构 package main import "fmt" func main() { /* 这是我的第一个简单的程序 */ fmt.Println("hello "+"World!") }

让我们来看下以上程序的各个部分:

第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包。

如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。

下一行 import “fmt” 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。

下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。

下一行 /…/ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 / 开头,并以 / 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。

下一行 fmt.Println(…) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。 使用 fmt.Print(“hello, world\n”) 可以得到相同的结果。 Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。

当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。

2、执行go程序

在终端cd到.go文件所在目录

1、输入命令 go run helloworld.go 并按回车执行代码

$ go run helloworld.go hello world!

2、我们还可以使用 go build 命令来生成二进制文件

$go build helloworld.go $ls helloworld helloworld.go $./helloworld hello world!

值得注意的是:

{不能单独放在一行,否则代码在运行时会产生错误。

3、Go的变量声明 1)声明变量并指定类型

声明变量的一般形式是使用 var 关键字:

var identifier type

实例

package main import "fmt" func main() { //声明单个变量并赋值 var a string = "syb" fmt.Println(a) }

运行结果

syb 2)声明变量不指定类型

若不指定类型,则根据值自行判定变量类型

var v_name = value

实例

package main import "fmt" func main() { var d = true fmt.Println(d) }

运行结果

true 3)省略var,使用:= v_name := value

intVal := 1 相等于:

var intVal int intVal =1 4)多变量声明 //以下是声明多个变量并赋值的4种写法 var a, b int = 1, 2 //第1种 var a, b int //第2种 a, b = 1, 2 var a, b = 1, 2 //第3种 a, b := 1, 2 //第4种 4、Go的常量

常量是一个在程序运行时,不会被修改的量。

常量的定义

常量的定义格式:

const identifier [type] = value //多常量声明 const c_name1, c_name2 [type] = value1, value2

你**可以省略[type]**来让编译器自动根据变量的值来确定变量的类型。

特殊常量—iota

iota是一个特殊常量,只能在常量表达式中使用。

iota 在 const关键字出现时将被重置为 0(const 内部的第一行之前),之后const 中每新增一行将使 iota 自增1(iota 可理解为 const 语句块中的行索引)。

示例

package main import "fmt" func main() { const ( a = iota //0 b //1 c //2 d = "ha" //独立值,iota += 1 e //"ha" iota += 1 f = 100 //iota +=1 g //100 iota +=1 h = iota //7,恢复计数 i //8 ) fmt.Println(a,b,c,d,e,f,g,h,i) }

运行结果

0 1 2 ha ha 100 100 7 8 5、Go的条件判断语句

if…else…语句很简单不再赘述。

switch语句

Go中的switch语句不同于C/C++/Java中的switch语句。

Go中的switch 语句执行的过程从上至下,直到找到匹配项,匹配项后面也不需要再加 break。

switch 默认情况下 case 最后自带 break 语句,匹配成功后就不会执行其他 case,如果我们需要执行后面的 case,可以使用 fallthrough 。

示例

package main import "fmt" func main() { var grade string var marks int = 90 //switch语句第1种写法 switch marks { case 90: grade = "A" case 80: grade = "B" case 50,60,70 : grade = "C" default: grade = "D" } //switch语句第2种写法 switch { case grade == "A" : fmt.Printf("优秀!\n" ) case grade == "B", grade == "C" : fmt.Printf("良好\n" ) case grade == "D" : fmt.Printf("及格\n" ) case grade == "F": fmt.Printf("不及格\n" ) default: fmt.Printf("差\n" ); } fmt.Printf("你的等级是 %s\n", grade ); }

运行结果

优秀! 你的等级是 A Type Switch

Type switch可用于判断某个变量的类型

实例:

package main import "fmt" func main() { var x interface{} switch i := x.(type) { case nil: fmt.Printf(" x 的类型 :%T",i) case int: fmt.Printf("x 是 int 型") case float64: fmt.Printf("x 是 float64 型") case func(int) float64: fmt.Printf("x 是 func(int) 型") case bool, string: fmt.Printf("x 是 bool 或 string 型" ) default: fmt.Printf("未知型") } }

运行结果:

x 的类型 : switch中的fallthrough关键字

使用 fallthrough 会强制执行后面的 case 语句,fallthrough 不会判断下一条 case 的表达式结果是否为 true。

实例:

package main import "fmt" func main() { switch { case false: fmt.Println("1、case 条件语句为 false") fallthrough case true: fmt.Println("2、case 条件语句为 true") fallthrough case false: fmt.Println("3、case 条件语句为 false") fallthrough case true: fmt.Println("4、case 条件语句为 true") case false: fmt.Println("5、case 条件语句为 false") fallthrough default: fmt.Println("6、默认 case") } }

运行结果:

2、case 条件语句为 true 3、case 条件语句为 false 4、case 条件语句为 true select语句

1、select 语句随机执行一个可运行的 case。

2、每个case都必须是一个通信

3、如果没有 case 可运行,且没有default语句,它将阻塞,直到有 case 可运行。

4、如果有多个 case 都可以运行,Select 会随机公平地选出一个执行。其他不会执行。

实例:

package main import "fmt" func main() { var c1, c2, c3 chan int var i1, i2 int select { case i1 = fmt.Printf("c3 is closed\n") } default: fmt.Printf("no communication\n") } }

运行结果:

no communication 6、Go的循环语句 for循环

Go语言的for循环有3种形式

1)如同c语言的for

实例:

package main import "fmt" func main() { sum := 0 for i := 0; i sum := 0 // 这样写也可以,更像 While 语句形式 for sum str := []string{"google", "qpple"} for i, s := range str { fmt.Println(i, s) } num := [6]int{1, 2, 3, 5} for i,x:= range num { fmt.Printf("第 %d 位 x 的值 = %d\n", i,x) } }

运行结果:

0 google 1 qpple 第 0 位 x 的值 = 1 第 1 位 x 的值 = 2 第 2 位 x 的值 = 3 第 3 位 x 的值 = 5 第 4 位 x 的值 = 0 第 5 位 x 的值 = 0 循环控制语句

break语句和continue语句比较简单,在此不再赘述。

goto语句我自己没用过,记录一下。

实例:

在变量 a 等于 15 的时候跳过本次循环并回到循环的开始语句 LOOP 处

package main import "fmt" func main() { /* 定义局部变量 */ var a int = 10 /* 循环 */ LOOP: for a /* 跳过迭代 */ a = a + 1 goto LOOP } fmt.Printf("a的值为 : %d\n", a) a++ } }

运行结果:

a的值为 : 10 a的值为 : 11 a的值为 : 12 a的值为 : 13 a的值为 : 14 a的值为 : 16 a的值为 : 17 a的值为 : 18 a的值为 : 19 7、Go的函数 函数定义 //返回类型可省略 func function_name( [parameter list] ) [return_types] { //函数体 }

实例:

/* 函数返回两个数的最大值 */ func max(num1, num2 int) int { var result int if (num1 > num2) { result = num1 } else { result = num2 } return result }

Go函数可返回多个值。

实例:

package main import "fmt" func swap(x, y string) (string, string) { return y, x } func main() { a, b := swap("Google", "apple") fmt.Println(a, b) }

运行结果:

apple Google 匿名函数

匿名函数是指不需要定义函数名的一种函数实现方式。

在Go里面,函数可以像普通变量一样被传递或使用,Go语言支持随时在代码里定义匿名函数。

匿名函数由一个不带函数名的函数声明和函数体组成。

匿名函数的优越性在于可以直接使用函数内的变量,不必申明。

实例:

package main import ( "fmt" "math" ) func main() { getSqrt := func(a float64) float64 { return math.Sqrt(a) } fmt.Println(getSqrt(4)) }

运行结果:

2 Go函数闭包

用的较少,先跳过

Go的方法

Go 语言中同时有函数和方法。

一个方法就是一个包含了接受者的函数,接受者可以是命名类型或者结构体类型的一个值或者是一个指针。

语法格式如下:

func (variable_name variable_data_type) function_name() [return_type]{ /* 函数体*/ }

实例:

package main import ( "fmt" ) // 定义结构体 type Circle struct { radius float64 } func main() { var c1 Circle c1.radius = 10.00 fmt.Println("圆的面积 = ", c1.getArea()) } // 该方法属于 Circle 类型对象中的方法 func (c Circle) getArea() float64 { //c.radius 即为 Circle 类型对象中的属性 return 3.14 * c.radius * c.radius }

运行结果:

圆的面积 = 314 8、Go的数组

声明一个数组:

//声明一个整型数组num var num [10] float32

声明数组并初始化:

var str1 = [3]string{"C", "go", "C++"} //或者 str2 := [3]string{"Python", "Java", "JavaScript"}

如果数组长度不确定,可以使用 … 代替数组的长度,也可以直接空掉,编译器会根据元素个数自行推断数组的长度:

var array1 = [...]float32{2000.0, 2.7, 12.0, 3.0} var array2 = []float32{2000.0, 2.7, 12.0, 3.0} //或者 array2 := [...]float32{2002.0, 2.72, 12.2, 3.3} array2 := []float32{2002.0, 2.72, 12.2, 3.3} 多维数组 //声明一个3维的整型数组 var threedim [3][3][3] int 9、Go的指针 基本用法

与C++的指针基本一致。

实例:

package main import "fmt" func main() { var a int= 20 //声明整型指针变量ip var ip *int //用&取变量a的地址 ip = &a fmt.Printf("a 变量的地址是: %x\n", &a ) fmt.Printf("ip 变量储存的指针地址: %x\n", ip ) fmt.Printf("*ip 变量的值: %d\n", *ip ) }

运行结果:

a 变量的地址是: c00000a118 ip 变量储存的指针地址: c00000a118 *ip 变量的值: 20 空指针

nil,与C++中的NULL概念一致。

指针数组 //整型指针数组的声明 var ptr [5]*int 双重指针 //双重指针的声明格式 var ptr **int; 10、Go的结构体

实例:

package main import "fmt" //使用type xxx struct语句定义结构体 type Books struct { title string author string subject string bookId int } func main() { //创建结构体的两种写法 book1 := Books{"goNote","syb","go",1} book2 := Books{title:"skyBook", author: "me",bookId: 0} //访问结构体成员 fmt.Println(book1.title) fmt.Println(book2) } 11、Go的切片(Slice)

Go 语言的切片是对数组的抽象。

Go 数组的长度不可改变,在特定场景中这样的集合就不太适用。

Go 中提供了一种灵活,功能强悍的内置类型切片(“动态数组”),与数组相比切片的长度是不固定的,可以追加元素,在追加时可能使切片的容量增大。

定义切片

你可以声明一个未指定大小的数组来定义切片:

var identifier []type

切片不需要说明长度。

切片声明后未初始化则为空切片,为nil,长度为0.

切片初始化 创建切片(未赋值)

使用 make() 函数来创建切片:

var slice1 []type = make([]type, len, [cap]) //也可以简写为 slice1 := make([]type, len, [cap])

make()函数中的,len是数组的长度,cap是切片的容量(最大长度),cap是可选参数。

直接初始化 s := []int{1,2,3} //此时cap=len=3 截取数组来初始化 //将 arr 中从下标 startIndex 到 endIndex-1 下的元素创建为一个新的切片。 s := arr[startIndex:endIndex] //startIndex默认指向数组的第一个元素 //endIndex - 1默认指向数组的最后一个元素 通过切片s初始化切片s1 s1 := s[startIndex:endIndex] len()和cap()函数

切片是可索引的。

切片可以由 len() 方法获取长度。

切片可以由**cap()**方法获取容量,即最大长度。

实例:

package main import "fmt" func main() { num1 := [6]int{1, 2, 3, 4, 5, 6} //创建一个num2的切片 var num2 = make([]int,3,5) //初始化切片 num2 = num1[1:4] printSlice(num2) } //打印切片信息函数 func printSlice(x []int){ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) }

运行结果:

len=3 cap=5 slice=[2 3 4] append() 和 copy() 函数

下面的代码演示了拷贝切片的 copy 方法和向切片追加新元素的 append 方法。

实例:

package main import "fmt" func main() { var numbers []int printSlice(numbers) // 向切片添加一个元素 numbers = append(numbers, 1) printSlice(numbers) // 同时添加多个元素 numbers = append(numbers, 2,3,4) printSlice(numbers) // 创建切片 numbers1 ,容量为原切片的两倍 numbers1 := make([]int, len(numbers), (cap(numbers))*2) // 拷贝 numbers 的内容到 numbers1 copy(numbers1,numbers) printSlice(numbers1) } //打印切片信息函数 func printSlice(x []int){ fmt.Printf("len=%d cap=%d slice=%v\n",len(x),cap(x),x) }

运行结果:

len=0 cap=0 slice=[] len=1 cap=1 slice=[1] len=4 cap=4 slice=[1 2 3 4] len=4 cap=8 slice=[1 2 3 4] 12、Go的Map(集合)

Map 是一种无序的键值对的集合。

Map 最重要的一点是通过 key 来快速检索数据,key 类似于索引,指向数据的值。

Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。

不过,Map 是无序的,我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。

Map的声明 //声明一个map var map_name map[key_type]value_type

仅声明而未进行初始化的map,为nil。

Map的初始化

使用make()函数初始化Map

//使用内建函数make() map_name := make(map[key_type]value_type) delete()函数

delete() 函数用于删除集合的元素, 参数为 map 和其对应的 key。

delete(map_name, key) Map的创建和使用

实例:

package main import "fmt" func main() { // 创建Map集合 var countryCapitalMap map[string]string // 初始化Map集合 countryCapitalMap = make(map[string]string) // 向Map插入key - value对,各个国家对应的首都 countryCapitalMap [ "Chinese" ] = "北京" countryCapitalMap [ "Italy" ] = "罗马" countryCapitalMap [ "Japan" ] = "东京" countryCapitalMap [ "India" ] = "新德里" // 使用键输出地图值 for country := range countryCapitalMap { fmt.Println(country, "首都是", countryCapitalMap [country]) } // 查看元素在集合中是否存在 capital, ok := countryCapitalMap [ "American" ] fmt.Println(ok) if (ok) { fmt.Println("American 的首都是", capital) } else { fmt.Println("American 的首都不存在") } }

运行结果:

Italy 首都是 罗马 Japan 首都是 东京 India 首都是 新德里 Chinese 首都是 北京 false American 的首都不存在 13、Go的range

Go 语言中 range 关键字用于 for 循环中迭代数组(array)、切片(slice)、通道(channel)或集合(map)的元素。

在数组和切片中它返回元素的索引和索引对应的值,在集合中返回 key-value 对。

实例:

package main import "fmt" func main() { //这是我们使用range去求一个slice的和。使用数组跟这个很类似 nums := []int{2, 3, 4} sum := 0 for _, num := range nums { sum += num } fmt.Println("sum:", sum) //在数组上使用range将传入index和值两个变量。上面那个例子我们不需要使用该元素的序号,所以我们使用空白符"_"省略了。有时侯我们确实需要知道它的索引。 for i, num := range nums { if num == 3 { fmt.Println("index:", i) } } //range也可以用在map的键值对上。 kvs := map[string]string{"a": "apple", "b": "banana"} for k, v := range kvs { fmt.Printf("%s -> %s\n", k, v) } //range也可以用来枚举字符串。第一个参数是字符的索引,第二个是字符的ASCII码。 for i, c := range "go" { fmt.Println(i, c) } }

运行结果:

sum: 9 index: 1 a -> apple b -> banana 0 103 1 111 14、Go的类型转换

Go 语言类型转换基本格式如下:

type_name(expression) //type_name 为类型 //expression 为需要转换的表达式 15、Go的接口(interface)

Go 语言提供了另外一种数据类型即接口,它把所有的具有共性的方法定义在一起,任何其他类型只要实现了这些方法就是实现了这个接口。

实例:

package main import ( "fmt" ) // 定义Phone接口 type Phone interface { call() } type NokiaPhone struct { } // 接口实现 func (nokiaPhone NokiaPhone) call() { fmt.Println("I am Nokia, I can call you!") } type IPhone struct { } // 接口实现 func (iPhone IPhone) call() { fmt.Println("I am iPhone, I can call you!") } func main() { var phone Phone phone = new(NokiaPhone) phone.call() phone = new(IPhone) phone.call() }

运行结果:

I am Nokia, I can call you! I am iPhone, I can call you! interface可以被任意的对象实现一个对象可以实现任意多个interface任意的类型都实现了空interface(我们这样定义:interface{}),也就是包含0个method的interface那么interface里面到底能存什么值呢?如果我们定义了一个interface的变量,那么这个变量里面可以存实现这个interface的任意类型的对象。 16、Go错误处理

Go 语言通过内置的错误接口提供了非常简单的错误处理机制。

error类型是一个接口类型,下面是它的定义:

type error interface { Error() string }

我们可以在编码中通过实现 error 接口类型来生成错误信息。

函数通常在最后的返回值中返回错误信息。使用 errors.New 可返回一个错误信息:

func Sqrt(f float64) (float64, error) { if f dividee int divider int } // 实现 error 接口 func (de *DivideError) Error() string { strFormat := ` 无法完成除法运算, 除数不能为0. 被除数: %d 除数: 0 ` return fmt.Sprintf(strFormat, de.dividee) } // 定义 int 类型除法运算的函数 func Divide(varDividee int, varDivider int) (result int, errorMsg string) { if varDivider == 0 { dData := DivideError{ dividee: varDividee, divider: varDivider, } errorMsg = dData.Error() return } else { return varDividee / varDivider, "" } } func main() { // 正常情况 if result, errorMsg := Divide(100, 10); errorMsg == "" { fmt.Println("100/10 = ", result) } // 当除数为零的时候会返回错误信息 if _, errorMsg := Divide(100, 0); errorMsg != "" { fmt.Println("错误信息: ", errorMsg) } }

运行结果:

100/10 = 10 错误信息: 无法完成除法运算, 除数不能为0. 被除数: 100 除数: 0 17、Go并发

Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。

goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。

goroutine 语法格式:

go 函数名( 参数列表 ) //例如: go f(x, y, z)

Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。

同一个程序中的所有 goroutine 共享同一个地址空间。

通道(channel)

通道(channel)是用来传递数据的一个数据结构。

声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:

ch := make(chan int, [缓冲区大小(可选)])

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。

操作符 sum += v } // 把 sum 发送到通道 c c 7, 2, 8, -9, 4, 0} c := make(chan int) go sum(s[:len(s)/2], c) go sum(s[len(s)/2:], c) // 从通道 c 中接收 x, y := c fmt.Println(i) } }

运行结果:

0 1 1 2 3 5 8 13 21 34


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3